# -*- coding: UTF-8 -*-
import functools
import json
from abc import ABCMeta, abstractmethod
import datetime
import os

from inspect import isfunction


def with_metaclass(meta, *bases):
    class metaclass(meta):
        __call__ = type.__call__
        __init__ = type.__init__

        def __new__(cls, name, this_bases, d):
            if this_bases is None:
                return type.__new__(cls, name, (), d)
            return meta(name, bases, d)

    return metaclass('temporary_class', None, {})


def super_init(init):
    def wapper(self, *args, **kwargs):
        global count
        print(self.__class__.__mro__)
        for _ in self.__class__.__mro__[1:-1]:
            _.__init__(self)
        init(self, *args, **kwargs)

    return wapper


# def validate(self, extra_validators=None):
#     if extra_validators is not None:
#         extra = extra_validators.copy()
#     else:
#         extra = {}
#
#     for name in self._fields:
#         inline = getattr(self.__class__, 'validate_%s' % name, None)
#
#         if inline is not None:
#             extra.setdefault(name, []).append(inline)
#
#     return super(Form, self).validate(extra)
import importlib


def dynamic_import(model_name, *attr):
    model = importlib.import_module(model_name)
    cls = []
    for _ in attr:
        if hasattr(model, _):
            cls.append(getattr(model, _))
        else:
            raise AttributeError("%s.%s" % (model_name, _))
    return cls[0] if len(attr) == 1 else cls


class Serializable(object):
    def to_json(self):
        pass

    def to_dict(self):
        pass
    # isinstance(t, SerializableMixin)


class LogItemTxt(list):
    indent_num = 2
    indent_str = ' '

    def __init__(self, **kwargs):
        super(LogItem, self).__init__()
        self.start = datetime.datetime.now()
        if 'tid' in kwargs:
            self.tid = kwargs.pop('tid', None)
            self.label = kwargs.pop('label', '')
        else:
            k, v = kwargs.popitem()
            self.label = k
            if v.__class__ is list:
                for i in v:
                    self.append(i)
            else:
                self.append(v)

    @property
    def lines(self):
        lines = ["%s--%s" % (self.label, self.start)]
        sign = LogItem.indent_num * LogItem.indent_str
        for item in self:
            if item.__class__ is LogItem:
                for i in item.lines:
                    lines.append(sign + i)
            else:
                lines.append(sign + str(item))
        lines.append("%s--%s" % (self.label, datetime.datetime.now()))
        return lines

    def write(self, dirs='./task_log/'):
        n = int(int(self.tid) / 1000)
        dirs += '/%s-%s' % (n * 1000, n * 1000 + 999)
        if not os.path.exists(dirs):
            os.makedirs(dirs)
        file_path = dirs + '/' + str(self.tid) + '.txt'

        with open(file_path, 'a') as f:
            f.write('\n')
            f.write('\n'.join(str(i) for i in self.lines))
            f.write('\n')


class LogEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, LogItem):
            return self.default(obj.to_dict())
        elif isinstance(obj, Serializable):
            return self.default(obj.to_dict())
        elif isfunction(obj):
            return 'function-%s' % obj.__name__
        elif isinstance(obj, datetime.date):
            return obj.strftime('%Y-%m-%d %H:%M:%S')
        elif isinstance(obj, dict):
            for k in obj:
                try:
                    if isinstance(obj[k], (datetime.datetime, datetime.date, Serializable)):
                        obj[k] = self.default(obj[k])
                    else:
                        obj[k] = obj[k]
                except TypeError:
                    obj[k] = None
            return obj
        # elif isinstance(obj, Pagination):
        #     return self.default(obj.items)
        return json.JSONEncoder.default(self, obj)


class LogItem:
    indent_num = 2
    indent_str = ' '

    def __init__(self, title='None', *args, **kwargs):
        self.__title = title
        self.__data = []
        self.push(*args, **kwargs)

    def to_dict(self):
        return {self.__title: self.__data}

    def append(self, v):
        if isinstance(v, Serializable):
            self.append(v.to_dict())
        else:
            self.__data.append(v)

    def push(self, *args, **kwargs):
        if args:
            self.push(args=args)
        for k, v in kwargs.items():
            if isinstance(v, Serializable):
                self.append({k: v.to_dict()})
            elif isinstance(v, dict):
                self.append({k: v})
            elif isinstance(v, list):
                self.push(**{'%s-%s' % (k, a): b for a, b in enumerate(v)})
            elif isinstance(v, LogItem):
                self.__data.append({k: v.to_dict()})
            elif isfunction(v):
                self.__data.append({k: v.__name__})
            else:
                self.__data.append({k: v})
        return self

    def write(self, tid=0):
        self.__title = "Task-%s" % tid
        n = int(int(tid) / 1000)
        path = task_log_path + ('%s-%s/' % (n * 1000, n * 1000 + 999))
        if not os.path.exists(path):
            os.makedirs(path)
        path = path + str(tid) + '.json'
        with open(path, 'w+', encoding='utf-8') as f:
            json.dump(self.to_dict(), f, indent=4, cls=LogEncoder)
        self.clear()

    def clear(self):
        self.__title = None
        self.__data.clear()


import time

# 注 机器ID占位5 这也就意味者十进制下编号不能超过31  将机器ID与机房ID合并，最大三个机房即00 10 20 每个机房的数值 + 1 即是机器ID  备用 30 31
WORKER_ID_BITS = 5
SEQUENCE_BITS = 12

# 最大取值计算
MAX_WORKER_ID = -1 ^ (-1 << WORKER_ID_BITS)

# 移位偏移计算
WORKER_ID_SHIFT = SEQUENCE_BITS
TIMESTAMP_LEFT_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS
# print(WORKER_ID_SHIFT, TIMESTAMP_LEFT_SHIFT)

# 序号循环掩码
SEQUENCE_MASK = -1 ^ (-1 << SEQUENCE_BITS)
# print(SEQUENCE_MASK)

# 起始时间
TWEPOCH = 1594977661913


class IdWorker(object):

    def __init__(self, worker_id, sequence=0):
        """
        :param worker_id: 机房和机器的ID 最大编号可为00 - 31  实际使用范围 00 - 29  备用 30 31
        :param sequence: 初始码
        """
        if worker_id > MAX_WORKER_ID or worker_id < 0:
            raise ValueError('worker_id值越界')

        self.worker_id = worker_id
        self.sequence = sequence
        self.last_timestamp = -1  # 上次计算的时间戳

    def get_timestamp(self):
        """
        生成毫秒级时间戳
        :return: 毫秒级时间戳
        """
        return int(time.time() * 1000)

    def wait_next_millis(self, last_timestamp):
        """
        等到下一毫秒
        """
        timestamp = self.get_timestamp()
        while timestamp <= last_timestamp:
            timestamp = self.get_timestamp()
        return timestamp

    def get_id(self):
        """"""
        timestamp = self.get_timestamp()
        # 判断服务器的时间是否发生了错乱或者回拨
        if timestamp < self.last_timestamp:
            # 如果服务器发生错乱 应该抛出异常
            # 此处待完善
            pass

        if timestamp == self.last_timestamp:
            self.sequence = (self.sequence + 1) & SEQUENCE_MASK
            if self.sequence == 0:
                timestamp = self.wait_next_millis(self.last_timestamp)
        else:
            self.sequence = 0
        self.last_timestamp = timestamp
        new_id = ((timestamp - TWEPOCH) << TIMESTAMP_LEFT_SHIFT) | (self.worker_id << WORKER_ID_SHIFT) | self.sequence
        return new_id




